Optimoi Djangon tietokantakyselyitä select_related- ja prefetch_related-metodeilla parantaaksesi suorituskykyä. Opi käytännön esimerkkejä ja parhaita käytäntöjä.
Django ORM -kyselyoptimointi: select_related vs. prefetch_related
Kun Django-sovelluksesi kasvaa, tehokkaista tietokantakyselyistä tulee ratkaisevan tärkeitä optimaalisen suorituskyvyn ylläpitämiseksi. Djangon ORM tarjoaa tehokkaita työkaluja tietokantahakujen minimoimiseksi ja kyselynopeuden parantamiseksi. Kaksi keskeistä tekniikkaa tämän saavuttamiseksi ovat select_related ja prefetch_related. Tämä kattava opas selittää nämä käsitteet, esittelee niiden käyttöä käytännön esimerkein ja auttaa sinua valitsemaan oikean työkalun omiin tarpeisiisi.
N+1-ongelman ymmärtäminen
Ennen kuin syvennymme select_related- ja prefetch_related-metodeihin, on tärkeää ymmärtää niiden ratkaisema ongelma: N+1-kyselyongelma. Tämä tapahtuu, kun sovelluksesi suorittaa yhden alkukyselyn hakeakseen joukon olioita ja tekee sen jälkeen lisäkyselyitä (N kyselyä, jossa N on olioiden lukumäärä) hakeakseen liittyvät tiedot kullekin oliolle.
Tarkastellaan yksinkertaista esimerkkiä malleista, jotka edustavat kirjailijoita ja kirjoja:
class Author(models.Model):
name = models.CharField(max_length=255)
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
Kuvittele nyt, että haluat näyttää listan kirjoista ja niiden kirjailijoista. Naiivi lähestymistapa voisi näyttää tältä:
books = Book.objects.all()
for book in books:
print(f"{book.title} by {book.author.name}")
Tämä koodi generoi yhden kyselyn kaikkien kirjojen hakemiseksi ja sen jälkeen yhden kyselyn jokaista kirjaa kohden sen kirjailijan hakemiseksi. Jos sinulla on 100 kirjaa, suoritat 101 kyselyä, mikä johtaa merkittävään suorituskykyrasitteeseen. Tämä on N+1-ongelma.
select_related-metodin esittely
select_related-metodia käytetään optimoimaan kyselyitä, jotka sisältävät yksi-yhteen- ja vierasavain-suhteita. Se toimii liittämällä (JOIN) liittyvät taulut alkuperäiseen kyselyyn, mikä hakee tehokkaasti liittyvät tiedot yhdellä tietokantahaulla.
Palataan kirjailija- ja kirjaesimerkkiimme. Poistaaksemme N+1-ongelman voimme käyttää select_related-metodia näin:
books = Book.objects.all().select_related('author')
for book in books:
print(f"{book.title} by {book.author.name}")
Nyt Django suorittaa yhden monimutkaisemman kyselyn, joka liittää Book- ja Author-taulut. Kun käytät silmukassa book.author.name-attribuuttia, tiedot ovat jo saatavilla, eikä ylimääräisiä tietokantakyselyitä suoriteta.
select_related-metodin käyttö useiden suhteiden kanssa
select_related voi kulkea useiden suhteiden läpi. Jos sinulla on esimerkiksi malli, jolla on vierasavain toiseen malliin, jolla puolestaan on vierasavain kolmanteen malliin, voit käyttää select_related-metodia hakeaksesi kaikki liittyvät tiedot yhdellä kertaa.
class Country(models.Model):
name = models.CharField(max_length=255)
class AuthorProfile(models.Model):
author = models.OneToOneField(Author, on_delete=models.CASCADE)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
# Add country to Author
Author.profile = models.OneToOneField(AuthorProfile, on_delete=models.CASCADE, null=True, blank=True)
authors = Author.objects.all().select_related('profile__country')
for author in authors:
print(f"{author.name} is from {author.profile.country.name if author.profile else 'Unknown'}")
Tässä tapauksessa select_related('profile__country') hakee AuthorProfile-olion ja siihen liittyvän Country-olion yhdellä kyselyllä. Huomaa kaksois alleviivaus (__) -notaatio, joka mahdollistaa suhdepuun läpi kulkemisen.
select_related-metodin rajoitukset
select_related on tehokkain yksi-yhteen- ja vierasavainsuhteiden kanssa. Se ei sovellu monta-moneen-suhteisiin tai käänteisiin vierasavainsuhteisiin, koska se voi johtaa suuriin ja tehottomiin kyselyihin käsiteltäessä suuria datajoukkoja. Näihin tilanteisiin prefetch_related on parempi valinta.
prefetch_related-metodin esittely
prefetch_related on suunniteltu optimoimaan kyselyitä, jotka sisältävät monta-moneen- ja käänteisiä vierasavain-suhteita. Liitosten (JOIN) sijaan prefetch_related tekee erilliset kyselyt kullekin suhteelle ja yhdistää tulokset Pythonissa. Vaikka tämä sisältää useita kyselyitä, se voi olla tehokkaampaa kuin suurten liitosten käyttö, kun käsitellään suuria liittyviä datajoukkoja.
Tarkastellaan tilannetta, jossa kullakin kirjalla voi olla useita genrejä:
class Genre(models.Model):
name = models.CharField(max_length=255)
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
genres = models.ManyToManyField(Genre)
Hakeaksemme listan kirjoista genreineen select_related ei olisi sopiva. Sen sijaan käytämme prefetch_related-metodia:
books = Book.objects.all().prefetch_related('genres')
for book in books:
genre_names = [genre.name for genre in book.genres.all()]
print(f"{book.title} ({', '.join(genre_names)}) by {book.author.name}")
Tässä tapauksessa Django suorittaa kaksi kyselyä: yhden hakeakseen kaikki kirjat ja toisen hakeakseen kaikki näihin kirjoihin liittyvät genret. Sen jälkeen se yhdistää Pythonin avulla genret tehokkaasti niiden omiin kirjoihin.
prefetch_related käänteisten vierasavainten kanssa
prefetch_related on hyödyllinen myös käänteisten vierasavainsuhteiden optimoinnissa. Tarkastellaan seuraavaa esimerkkiä:
class Author(models.Model):
name = models.CharField(max_length=255)
country = models.CharField(max_length=255, blank=True, null=True) # Added for clarity
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Author, related_name='books', on_delete=models.CASCADE)
Hakeaksesi listan kirjailijoista ja heidän kirjoistaan:
authors = Author.objects.all().prefetch_related('books')
for author in authors:
book_titles = [book.title for book in author.books.all()]
print(f"{author.name} has written: {', '.join(book_titles)}")
Tässä prefetch_related('books') hakee kaikki kuhunkin kirjailijaan liittyvät kirjat erillisellä kyselyllä, välttäen N+1-ongelman, kun käytetään author.books.all()-metodia.
prefetch_related-metodin käyttö querysetin kanssa
Voit mukauttaa prefetch_related-metodin toimintaa entisestään antamalla sille mukautetun querysetin liittyvien olioiden hakemiseen. Tämä on erityisen hyödyllistä, kun haluat suodattaa tai järjestää liittyviä tietoja.
from django.db.models import Prefetch
authors = Author.objects.prefetch_related(Prefetch('books', queryset=Book.objects.filter(title__icontains='django')))
for author in authors:
django_books = author.books.all()
print(f"{author.name} has written {len(django_books)} books about Django.")
Tässä esimerkissä Prefetch-olio antaa meille mahdollisuuden määrittää mukautetun querysetin, joka hakee vain ne kirjat, joiden otsikot sisältävät sanan "django".
prefetch_related-metodin ketjutus
Samoin kuin select_related-metodin, voit ketjuttaa prefetch_related-kutsuja optimoidaksesi useita suhteita:
authors = Author.objects.all().prefetch_related('books__genres')
for author in authors:
for book in author.books.all():
genres = book.genres.all()
print(f"{author.name} wrote {book.title} which is of genre(s) {[genre.name for genre in genres]}")
Tämä esimerkki esihakee kirjailijaan liittyvät kirjat ja sen jälkeen näihin kirjoihin liittyvät genret. Ketjutettu prefetch_related mahdollistaa syvälle sisäkkäisten suhteiden optimoinnin.
select_related vs. prefetch_related: Oikean työkalun valinta
Joten, milloin sinun tulisi käyttää select_related- ja milloin prefetch_related-metodia? Tässä on yksinkertainen ohjenuora:
select_related: Käytä yksi-yhteen- ja vierasavainsuhteisiin, kun sinun tarvitsee päästä käsiksi liittyviin tietoihin usein. Se suorittaa tietokannassa liitoksen (JOIN), joten se on yleensä nopeampi pienten tietomäärien noutamisessa.prefetch_related: Käytä monta-moneen- ja käänteisiin vierasavainsuhteisiin, tai kun käsittelet suuria liittyviä datajoukkoja. Se suorittaa erillisiä kyselyitä ja käyttää Pythonia tulosten yhdistämiseen, mikä voi olla tehokkaampaa kuin suuret liitokset. Käytä myös silloin, kun tarvitset mukautettua queryset-suodatusta liittyville olioille.
Yhteenvetona:
- Suhdetyyppi:
select_related(ForeignKey, OneToOne),prefetch_related(ManyToManyField, käänteinen ForeignKey) - Kyselytyyppi:
select_related(JOIN),prefetch_related(Erilliset kyselyt + Python-liitos) - Datan koko:
select_related(Pieni liittyvä data),prefetch_related(Suuri liittyvä data)
Käytännön esimerkkejä ja parhaita käytäntöjä
Tässä muutamia käytännön esimerkkejä ja parhaita käytäntöjä select_related- ja prefetch_related-metodien käyttöön todellisissa tilanteissa:
- Verkkokauppa: Kun näytät tuotetietoja, käytä
select_related-metodia hakeaksesi tuotteen kategorian ja valmistajan. Käytäprefetch_related-metodia hakeaksesi tuotekuvat tai liittyvät tuotteet. - Sosiaalinen media: Kun näytät käyttäjän profiilia, käytä
prefetch_related-metodia hakeaksesi käyttäjän julkaisut ja seuraajat. Käytäselect_related-metodia noutaaksesi käyttäjän profiilitiedot. - Sisällönhallintajärjestelmä (CMS): Kun näytät artikkelia, käytä
select_related-metodia hakeaksesi kirjoittajan ja kategorian. Käytäprefetch_related-metodia hakeaksesi artikkelin tagit ja kommentit.
Yleiset parhaat käytännöt:
- Profiloi kyselysi: Käytä Djangon debug-työkalupalkkia tai muita profilointityökaluja tunnistaaksesi hitaat kyselyt ja mahdolliset N+1-ongelmat.
- Aloita yksinkertaisesti: Aloita naiivilla toteutuksella ja optimoi sitten profilointitulosten perusteella.
- Testaa perusteellisesti: Varmista, että optimointisi eivät aiheuta uusia bugeja tai suorituskyvyn heikkenemistä.
- Harkitse välimuistia: Usein käytetylle datalle kannattaa harkita välimuistimekanismeja (esim. Djangon välimuistikehys tai Redis) suorituskyvyn parantamiseksi entisestään.
- Käytä indeksejä tietokannassa: Tämä on välttämätöntä optimaalisen kyselysuorituskyvyn kannalta, erityisesti tuotannossa.
Edistyneet optimointitekniikat
select_related- ja prefetch_related-metodien lisäksi on olemassa muita edistyneitä tekniikoita, joita voit käyttää Django ORM -kyselyidesi optimointiin:
only()jadefer(): Näiden metodien avulla voit määrittää, mitkä kentät noudetaan tietokannasta. Käytäonly()-metodia noutaaksesi vain tarvittavat kentät jadefer()-metodia jättääksesi pois kentät, joita ei tarvita välittömästi.values()javalues_list(): Nämä metodit mahdollistavat datan noutamisen sanakirjoina tai tupleina Django-malli-instanssien sijaan. Tämä voi olla tehokkaampaa, kun tarvitset vain osan mallin kentistä.- Raaka-SQL-kyselyt: Joissakin tapauksissa Djangon ORM ei välttämättä ole tehokkain tapa noutaa dataa. Voit käyttää raakoja SQL-kyselyitä monimutkaisiin tai erittäin optimoituihin kyselyihin.
- Tietokantakohtaiset optimoinnit: Eri tietokannoilla (esim. PostgreSQL, MySQL) on erilaisia optimointitekniikoita. Tutki ja hyödynnä tietokantakohtaisia ominaisuuksia parantaaksesi suorituskykyä entisestään.
Kansainvälistämiseen liittyviä huomioita
Kehitettäessä Django-sovelluksia maailmanlaajuiselle yleisölle on tärkeää ottaa huomioon kansainvälistäminen (i18n) ja lokalisointi (l10n). Tämä voi vaikuttaa tietokantakyselyihisi monin eri tavoin:
- Kielikohtainen data: Saatat joutua tallentamaan sisällön käännöksiä tietokantaasi. Käytä Djangon i18n-kehystä käännösten hallintaan ja varmista, että kyselysi noutavat datan oikean kieliversion.
- Merkistöt ja lajittelujärjestykset: Valitse tietokantaasi sopivat merkistöt ja lajittelujärjestykset tukeaksesi laajaa valikoimaa kieliä ja merkkejä.
- Aikavyöhykkeet: Kun käsittelet päivämääriä ja aikoja, ole tietoinen aikavyöhykkeistä. Tallenna päivämäärät ja ajat UTC-ajassa ja muunna ne käyttäjän paikalliseen aikavyöhykkeeseen niitä näytettäessä.
- Valuutan muotoilu: Kun näytät hintoja, käytä asianmukaisia valuuttasymboleita ja muotoiluja käyttäjän paikallisasetusten mukaan.
Yhteenveto
Django ORM -kyselyiden optimointi on välttämätöntä skaalautuvien ja suorituskykyisten verkkosovellusten rakentamisessa. Ymmärtämällä ja käyttämällä tehokkaasti select_related- ja prefetch_related-metodeja voit vähentää merkittävästi tietokantakyselyiden määrää ja parantaa sovelluksesi yleistä reagointikykyä. Muista profiloida kyselysi, testata optimointisi perusteellisesti ja harkita muita edistyneitä tekniikoita suorituskyvyn parantamiseksi entisestään. Noudattamalla näitä parhaita käytäntöjä voit varmistaa, että Django-sovelluksesi tarjoaa sujuvan ja tehokkaan käyttökokemuksen sen koosta tai monimutkaisuudesta riippumatta. Muista myös, että hyvä tietokantasuunnittelu ja oikein konfiguroidut indeksit ovat välttämättömiä optimaalisen suorituskyvyn varmistamiseksi.